#include "StdAfx.h"
#include "arc_2.h"
#include "fast_trigonometries.h"
#include <limits>
#include <algorithm>
#include <boost\foreach.hpp>

point_2 arc_2::start_point(void) const
{
  return point_2(center_x+radius*kCos[start_angle],
    center_y-radius*kSin[start_angle]);
}

point_2 arc_2::end_point(void) const
{
  return point_2(center_x+radius*kCos[end_angle],
    center_y-radius*kSin[end_angle]);
}

int arc_2::degree(void) const
{
  return angle_math::sweep(start_angle, end_angle);
}

double arc_2::radian(void) const
{
  return angle_math::radian(degree());
}

box_2 arc_2::bounds(void) const
{
  boost::array<point_2, 6> vertexes = {start_point(), start_point(),
    start_point(), start_point(), start_point(), end_point()};

  if (angle_math::contain(0, start_angle, end_angle)) {
    vertexes[0].x = center_x + radius;
    vertexes[0].y = center_y;
  }

  if (angle_math::contain(90, start_angle, end_angle)) {
    vertexes[1].x = center_x;
    vertexes[1].y = center_y - radius;
  }

  if (angle_math::contain(180, start_angle, end_angle)) {
    vertexes[2].x = center_x - radius;
    vertexes[2].y = center_y;
  }

  if (angle_math::contain(270, start_angle, end_angle)) {
    vertexes[3].x = center_x;
    vertexes[3].y = center_y + radius;
  }

  double left    = std::numeric_limits<double>::max();
  double right   = std::numeric_limits<double>::min();
  double top     = std::numeric_limits<double>::max();
  double bottom  = std::numeric_limits<double>::min();

  BOOST_FOREACH(const point_2& vertex, vertexes)
  {
    left    = std::min(left, vertex.x);
    right   = std::max(left, vertex.x);
    top     = std::min(left, vertex.y);
    bottom  = std::max(left, vertex.y);
  }

  return box_2(left, top, right, bottom);
}

bool arc_2::adjacent(const point_2& point, int tolerance) const
{
  double lowerRadius = std::max(radius-tolerance, 0.0);
  double upperRadius = radius + tolerance;

  point_2   center(center_x, center_y);
  vector_2  vec       = point - center;
  int       vecAngle  = vec.angle();

  return (point.distance2(center)<upperRadius*upperRadius) &&
    (point.distance2(center)>lowerRadius*lowerRadius) &&
    angle_math::contain(vecAngle, start_angle, end_angle);
}

bool arc_2::intersects(const box_2& box) const
{
  boost::array<point_2, 6> vertexes = {start_point(), start_point(),
    start_point(), start_point(), start_point(), end_point()};

  if (angle_math::contain(0, start_angle, end_angle)) {
    vertexes[0].x = center_x + radius;
    vertexes[0].y = center_y;
  }

  if (angle_math::contain(90, start_angle, end_angle)) {
    vertexes[1].x = center_x;
    vertexes[1].y = center_y - radius;
  }

  if (angle_math::contain(180, start_angle, end_angle)) {
    vertexes[2].x = center_x - radius;
    vertexes[2].y = center_y;
  }

  if (angle_math::contain(270, start_angle, end_angle)) {
    vertexes[3].x = center_x;
    vertexes[3].y = center_y + radius;
  }

  BOOST_FOREACH(const point_2& vertex, vertexes)
  {
    if (box.within(vertex)) {
      return true;
    }
  }

  return false;
}

bool arc_2::within(const point_2& point) const
{
  point_2   center(center_x, center_y);
  vector_2  vec       = point - center;
  int       vecAngle  = vec.angle();

  return point.distance2(center)<radius*radius &&
    angle_math::contain(vecAngle, start_angle, end_angle);
}
